﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;

namespace ManPowerEngine
{
	/// <summary>
	/// Render System of MPE
	/// </summary>
	public class RenderSystem
	{
		/// <summary>
		/// The position of the camera
		/// </summary>
		public static Vector CameraPosition = new Vector(0, 0);

		private static RenderSystem msRenderSystem;
		private static Canvas msCanvas;
		private static int msWidth;
		private static int msHeight;
		internal static string msAssemblyName;

		/// <summary>
		/// All renderers that registered in the system
		/// </summary>
		private List<RendererBase> mRegisteredRenderers;
		/// <summary>
		/// The overlay renderers, they are rendered relative the screen coordination.
		/// </summary>
		private List<RendererBase> mRegisteredOverlayRenderers;

		internal RenderSystem(Canvas canvas, string assemblyname, int width, int height)
		{
			msRenderSystem = this;
			msCanvas = canvas;
			msWidth = width;
			msHeight = height;
			msAssemblyName = assemblyname;
			mRegisteredRenderers = new List<RendererBase>();
			mRegisteredOverlayRenderers = new List<RendererBase>();
		}

		/// <summary>
		/// get the instance of render system
		/// </summary>
		public static RenderSystem Instance
		{
			get { return msRenderSystem; }
		}

		/// <summary>
		/// get the target canvas
		/// </summary>
		public static Canvas Canvas
		{
			get { return msCanvas; }
		}

		/// <summary>
		/// Roughly check if this object is on-screen or not
		/// </summary>
		/// <param name="pos">The location of this object relative to the camera</param>
		/// <param name="width">width</param>
		/// <param name="height">height</param>
		/// <returns>On-screen or not</returns>
		public static bool OnScreenCheck(Vector pos, int width, int height)
		{
			// max of width/height: greedy.
			int maxdimension = Math.Max(width, height);
			if (pos.X > msWidth ||
				pos.X < -maxdimension ||
				pos.Y > msHeight ||
				pos.Y < -maxdimension)
			{
				return false;
			}
			return true;
		}

		/// <summary>
		/// render the frame from the game objects hierarchy
		/// </summary>
		internal void RenderFrame()
		{
			// remove everything from the last frame
			List<RendererBase> previouslist = mRegisteredRenderers;
			mRegisteredRenderers = new List<RendererBase>();

			List<RendererBase> previousoverlaylist = mRegisteredOverlayRenderers;
			mRegisteredOverlayRenderers = new List<RendererBase>();

			// add everything currently in the hierarchy into render list, and remove them from the previous list (to-be-deleted list)
			_AddGameObjectIntoList(GameObject.Root, mRegisteredRenderers, previouslist);
			_AddGameObjectIntoList(GameObject.OverlayRoot, mRegisteredOverlayRenderers, previousoverlaylist);

			// delete the remaining from the screen
			foreach (RendererBase renderer in previouslist)
			{
				renderer.CleanUp(msCanvas);
				mRegisteredRenderers.Remove(renderer);
			}
			foreach (RendererBase renderer in previousoverlaylist)
			{
				renderer.CleanUp(msCanvas);
				mRegisteredOverlayRenderers.Remove(renderer);
			}

			// update render objects them in render target canvas
			List<RendererBase> tobehide = new List<RendererBase>();
			foreach (RendererBase renderer in mRegisteredRenderers)
			{
				Vector pos = renderer.GameObject.Transform.Position - renderer.Pivot - CameraPosition;
				if (OnScreenCheck(pos, renderer.Width, renderer.Height))
				{
					renderer.UpdateRenderObjects(msCanvas, pos);
				}
				else
				{
					// off-screen, to be clean up.
					tobehide.Add(renderer);
				}
			}
			foreach (RendererBase renderer in tobehide)
			{
				renderer.CleanUp(msCanvas);
				mRegisteredRenderers.Remove(renderer);
			}
			// update overlay render objects them in render target canvas
			foreach (RendererBase renderer in mRegisteredOverlayRenderers)
			{
				Vector pos = renderer.GameObject.Transform.Position - renderer.Pivot;
				renderer.UpdateRenderObjects(msCanvas, pos);
			}
		}

		private void _AddGameObjectIntoList(GameObject go, List<RendererBase> list, List<RendererBase> lastframelist)
		{
			if (!go.Enabled)
				return;
			// recursively add all renderers into the list
			if (go.Renderer != null && go.Renderer.Visible)
			{
				list.Add(go.Renderer);
				if (lastframelist.Contains(go.Renderer))
				{
					lastframelist.Remove(go.Renderer);
				}
				else
				{
					go.Renderer.AddToCanvas(msCanvas);
				}
			}
			// visit all children
			foreach (GameObject child in go.Children)
			{
				_AddGameObjectIntoList(child, list, lastframelist);
			}
		}

		internal void CleanUp()
		{
			msCanvas = null;
		}
	}
}
